Skip to content

chore(platform)!: update rust-dashcore (incl secp256k1 0.33) - #5307

Merged
lklimek merged 23 commits into
v5.1-devfrom
chore/rust-dashcore-1112-v5.1
Oct 14, 2026
Merged

lklimek merged 23 commits into
v5.1-devfrom
chore/rust-dashcore-1112-v5.1

Conversation

@lklimek

@lklimek lklimek commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Basic explanation

What this does: Pins rust-dashcore to dev at 90359ed58a993bab0d4e267b4029bab132a56532, which includes rust-dashcore PR #1149, restoring legacy BLS public-key and canonical PlatformNodeId serialization and preserving the pre-v4 coinbase payload Serde layout. This is a port of #4932's migration to v5.1-dev.

Value: Gives #5150 a separately reviewable dependency base, so its diff focuses on wallet history and persistence repairs. #4932 and its existing dependent PRs remain unchanged.

Risks: This changes public Rust cryptography APIs and introduces a WebCrypto runtime requirement for WASM; the Node.js >=22 package requirements are being split into separate PR #5310. The merged base supplies the versioned Core v24 behavior. The SPV lifecycle and Node runtime requirements are addressed in stacked #5311 and #5310, which must land with this migration.

Issue being fixed or feature implemented

Port of #4932 from v5.0-dev to v5.1-dev, retaining its commit ancestry. Dependency base for #5150; no SQLite accounting migration, wallet history replay, or SwiftData accounting repair is included here.

What was done?

  • Stabilize test-suite proof verification across quorum rotation: on the specific missing-cache-key error, wait 65 seconds for sidecar publication and initial SDK bans to expire, then retry the proved read once. No transition rebroadcast or proof-validation bypass. The underlying service timing is tracked in quorum-list-server#16.
  • Port the secp256k1 0.33 API adaptations and WASM random-source configuration from chore(platform)!: bump rust-dashcore to 719de34b (secp256k1 0.33) #4932. Node.js package requirements and related README changes are excluded from this PR and will be raised directly to >=22 in a separate PR.

  • Pin all eight rust-dashcore workspace dependencies and all 13 upstream lockfile packages to dev at 90359ed58a993bab0d4e267b4029bab132a56532. This revision also carries rust-dashcore feat: adapt for web extension #1155 (a special payload is decoded only from transaction version 3), which v5.1-dev relies on since fix(platform)!: decode Core v24 blocks and transactions #5392. Cargo.lock uses the same revision for every upstream crate. The dash-pkc base-sdk pin moves to 33fcc59e1868c803f9608e0e78ee8bddf3abcee7, the revision that rust-dashcore dev uses, so the lockfile keeps a single base-sdk revision.

  • Adapt to the upstream changes that dev adds on top of feat: fetch data contract history endpoint #1149: compact ECDSA signatures go through dashcore::sign_message::MessageSignature (the CompactSignature trait is removed); DAPI drops its mapping for the removed dashcore_rpc::Error::Secp256k1; the masternode record, tracking and service-update code is the one v5.1-dev gained with fix(platform)!: decode Core v24 blocks and transactions #5392, built here against dev's API; WalletInfoInterface::truncate_above and note_chain_lock_height are delegated to the Core wallet; the trusted context provider keeps rejecting an identity-point quorum key through the new dpp::bls::PublicKey::is_identity(); WalletEvent::ChainTruncated updates the displayed balance.

  • Adapt wasm-dpp2 private-key hex decoding to the removal of dashcore_hashes::hex, using its existing hex dependency.

  • Update the native wallet FFI node-ID helper to the explicit EddsaPkHash conversion, preserving canonical SHA256-prefix bytes and the C ABI. A fixed-vector test and invalid-input/no-write checks cover the exported function.

  • Adapt Core crypto, SPV and spent-height query APIs. Use the shared BLS backend through dpp::bls, convert Ed25519 byte keys through the public operational-key API, preserve canonical node-ID bytes, and read owned masternode snapshots through the public SPV queries. The branch includes the SPV lifecycle fixes from fix(platform-wallet): retain SPV client and track shutdown through cancellation #5311 and fix(platform-wallet)!: tell a retryable SPV stop from one that needs a restart #5322.

  • Remove DPP's core_key_wallet_bip_38 and SDK's core_key_wallet_bip38 forwards because upstream removed BIP38 support. Adapt raw hash extraction and coinbase test fixtures to the newer upstream API.

  • Incorporate v5.1-dev through 054563c73e, including test(platform): freeze historical storage and signing vectors #5359's frozen vectors, common historical generation script and encryption test coverage in CI. Retain the migrated test APIs and additional BLS assertions. Keep the Core v24 identity, payout, port-resolution and persistence implementations from feat(platform)!: support Core v24 masternode identities at protocol version 14 #5227/feat(drive-abci)!: resolve Core v24 platform ports at protocol version 14 #5228 unchanged relative to the base. Remove this PR's superseded strict masternode conversions and legacy projection adapters.

  • Use the shared dash-pkc BLS backend with registry blst 0.3.17; blsful, blstrs_plus, and the workspace blst patch are removed. DPP's bls-signatures, core_verification and core_quorum_validation features directly require blst = "0.3.17" (>=0.3.17,<0.4), so external consumer lockfiles cannot retain incompatible 0.3.12. A TODO removes this temporary constraint once the pinned rust-dashcore graph enforces a compatible minimum.

  • Isolate quorum-key persistence from crypto Serde with an explicit fixed-48-byte storage adapter. Preserve V1 current keys and V2 current/previous keys, with no disk-format change or migration. Frozen fixtures generated by pre-migration commit 7c73e983b4 cover quorum records, full saved-state records and checkpoint state.

In-place changes to shipped generations

The existing generation methods receive mechanical secp256k1 API adaptations with unchanged digest construction, signature rules and canonical byte representations. Masternode processing, persistence and protocol-version tables now match the base branch; the historical/PV14 boundary comes from #5227/#5228. No additional consensus behavior change is intended by the local API adaptations.

choose_quorum/v0 (both selection helpers) and signature_verification_quorum_set/v0/quorums.rs keep their score bytes unchanged for all selecting protocol versions (1–14): the old From<Hash> for [u8; 32] and Hash::to_byte_array() both return the same inner array without reversal. Existing quorum-selection tests cover the adapted code.

QuorumForSavingV1 retains its exact 48 compressed key bytes without a length prefix for every protocol version that reads or writes it. The adapter uses the same validated DPP key parser and leaves the hash, index, quorum ordering and metadata unchanged. V1's C++-encoded previous quorums remain unchanged; V2 applies the adapter to both lists. Historical byte-for-byte fixture tests verify this storage-only refactor; it changes neither consensus outputs nor the disk format.

Address-witness verification (PlatformAddress::verify_bytes_against_witness) and the Core-signer path (sign_with_core_signer) switch from the removed CompactSignature trait to MessageSignature. The accept/reject set is unchanged: both parsers require 65 bytes with a header in 27..=34 and map the header to the same recovery id and compression flag, and MessageSignature::new(sig, true).serialize() produces the bytes to_compact_signature(true) did. Only the text of the parse error differs; it reaches InvalidStateTransitionSignatureError.message, not the error code. The frozen signing vectors from #5359 pass unchanged.

Merge order

This branch pins rust-dashcore dev; rust-dashcore PR #1149 is merged there.

Land this migration together with the Node runtime requirements in #5310 and SPV lifecycle adaptation in #5311. #5150 remains stacked above this PR, but must retain or restore dashpay/rust-dashcore#1112 separately: that PR is based on the dev revision pinned here, and #5150 uses its additional API. #5307 itself does not require #1112.

How Has This Been Tested?

Validation of the #5359 integration and Core-only BLS constraint (674465874a), retaining #5366 at f29c092fcf:

  • The quorum verification-status regression test passes on the combined tree; the newer remote wallet/SPV changes are preserved unchanged.
  • 62 targeted compatibility/quorum/storage tests, scoped all-target/all-feature Clippy for DPP/Drive ABCI/platform-encryption, formatting and ShellCheck passed.
  • Both external minimal Core feature configurations reproduced the missing aggregate-verification methods with existing blst 0.3.12 lockfiles. After the fix, cargo update -p dpp selects 0.3.17, forcing 0.3.12 is rejected, and each Core/BLS dependency graph passes WASM Clippy.
  • Full minimal DPP builds still fail on independent unconditional state_transition imports when state-transitions is disabled; those builds are not claimed green.

Validation after merging v5.1-dev (118830417e), on rust-dashcore dev (90359ed5), Platform commit 912e06b19b:

  • Workspace Clippy passed with the CI flags (--workspace --all-targets --all-features --locked -- --no-deps -D warnings).
  • platform-wallet and platform-wallet-storage: 2,235 tests passed, including the version-3 masternode tests and the stored-transaction-row fixture, which is unchanged.
  • Targeted: the fix(platform)!: decode Core v24 blocks and transactions #5392 asset-lock tests that rewrite the transaction version, drive-abci rpc::core (5), voting_default_limits and nonunique_merged_proof against their committed corpus and vectors (12), the trusted context provider and proof verifier suites, and the wasm-sdk context-provider and broadcast tests (11). wasm-sdk tests also type-check for wasm32-unknown-unknown.
  • Cargo.lock holds no package at a version that neither merge parent has, apart from the rust-dashcore revision.
  • Not run locally after the merge: the full drive-abci, rs-dapi, rs-sdk and strategy test suites; CI covers them.

Earlier validation on rust-dashcore dev (d036d844), Platform commit 9183842643:

  • Workspace Clippy passed with the CI flags (--workspace --all-targets --all-features --locked -- --no-deps -D warnings), before three test-only additions; platform-wallet was linted again after them.
  • dpp: 5,524 tests passed, including the frozen BLS, historical hash/signature and DashPay ciphertext vectors and the address-witness tests.
  • platform-wallet, platform-wallet-ffi, platform-wallet-storage, rs-unified-sdk-jni: 2,947 tests passed. platform-encryption, rs-dapi, simple-signer: 353 passed. drive-abci platform_types (quorum and saved-state storage): 126 passed.
  • Three new tests cover WalletEvent::ChainTruncated: no spend is reported, no payment hook is driven, and nothing is persisted yet.
  • Not run locally on this head: JS/WASM builds and tests, Swift, Kotlin, Docker and the test suite; CI covers them.

Validation on rust-dashcore PR #1149 (8fe0a381), Platform commit f475f72a11:

  • 62 targeted tests passed: 8 frozen BLS tests, 4 historical transaction/hash/signature tests, 4 DashPay encryption tests, and 46 quorum/storage tests including historical saved states and checkpoints.

  • Native all-target/all-feature Clippy passed for dpp, platform-encryption, drive-abci, platform-wallet, platform-wallet-storage, dash-sdk, and wasm-dpp2 with --locked -- --no-deps -D warnings.

  • wasm-dpp2 library Clippy passed on wasm32-unknown-unknown with all features. The additional wasm32 test-harness check fails on use super::TS_TYPES in src/group/token_event.rs:149; that file is unchanged from the base. Native test-target linting passes. JavaScript/browser runtime tests were not rerun locally.

  • Formatting, whitespace and pin-consistency checks passed; unrelated lockfile packages are unchanged.

  • External DPP consumer regression on 069a5cab6a: an existing lockfile selected blst 0.3.12 before the constraint; cargo update -p dpp now selects 0.3.17. Fresh resolution also selects 0.3.17, and explicitly selecting 0.3.12 is rejected. WASM all-target Clippy passes with bls-signatures and state-transitions; DPP formatting passes. cargo-machete is unavailable locally; CI performs that check.

  • Proof-verifier regression: all nine unit tests and the full package lint pass in CI. Four new cases cover delayed quorum publication/address availability, persistent failure, affected-state proofs, and invalid signatures. The main Test Suite passes with 95 tests; both browser suites and package functional tests pass on c1c7b095c2.

Validation on e6873e95:

  • Native FFI CI repair: reproduced the missing-method compiler failure, then passed both new platform-wallet-ffi regression tests and all-target/all-feature Clippy for platform-wallet-ffi, rs-unified-sdk-jni and rs-unified-sdk-ffi.
  • drive-abci all-target/all-feature Clippy, formatting and whitespace checks passed after the quorum adapter change.
  • 47 quorum/storage tests passed, including four new regression tests over five frozen historical fixtures, existing testnet/devnet saved states and the historical serialization fingerprint.
  • 206 selected tests passed across platform-wallet, platform-wallet-storage and drive-abci: provider-key vectors, masternode tracking and service updates, SPV lifecycle and peer classification, quorum selection, coinbase credit-pool decoding, SQLite persistence and buffer semantics.
  • Clippy passed for platform-wallet, platform-wallet-storage, dash-sdk and drive-abci with --all-targets --all-features --locked --offline -- --no-deps -D warnings.
  • Locked Cargo test resolution, formatting, whitespace checks, and consistent pins across the eight direct dependencies and 13 lockfile packages passed.

CI on c1c7b095c2 passes Rust workspace tests, Kotlin, JS/WASM builds and tests, Docker builds, browser suites, Dashmate E2E and package functional tests (45 successful checks). Swift compiled the Rust library but failed when copying it: No space left on device on self-hosted mac-runner-pasta. Swift remains unverified on this head until runner disk space is restored and its job is rerun.

Outstanding review blockers

  • WASM Node runtime requirement: Node version declarations have been removed from this PR at the author's request. PR #5310 supplies the Node >=22 package requirements, documentation and matching Docker runtime; this dependency must be landed together with the migration.

  • Quorum storage is covered by frozen pre-migration records. These fixtures do not establish compatibility for every historical wallet transaction or masternode record layout.

Breaking Changes

Public Core/secp256k1 Rust types and methods change; Platform consumers are adapted here. The migrated WASM entropy backend requires global WebCrypto; the Node.js >=22 minimum and related package/documentation changes are handled in a separate PR. The unified C API loses bip38_encrypt_private_key and bip38_decrypt_private_key (unsupported-operation stubs that upstream removed); direct C and Swift callers must drop their calls. The rest of the FFI ABI is unchanged. Upstream restores legacy BLS public-key hex serialization and canonical PlatformNodeId bytes, and preserves the pre-v4 coinbase Serde shape. No database migration is included; compatibility with every historical record layout, or with records written by the intermediate raw-BLS/reversed-node-ID implementation, is not established by this pin. DPP's core_key_wallet_bip_38 and SDK's core_key_wallet_bip38 features are removed because upstream removed BIP38.

Known limitations

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have added "!" to the title and described breaking changes in the corresponding section if my code contains any
  • I have made corresponding changes to the documentation if needed
  • If I added or changed GroveDB structure, I described it in the area's structure.rs, regenerated grovedb-structure.json, and checked the structure viewer link posted on this pull request

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

🤖 Co-authored by Claudius the Magnificent AI Agent

PR Hygiene · 9774c72

  • Bots — coderabbitai not yet · thepastaclaw ✓ — /skip-bots proceeds without the ones not yet reported
  • Self-review — address PastaPastaPasta left a review thread unresolved, then post /self-reviewed
  • Build green
  • Approvals
    • files with no dedicated owner (.devcontainer/devcontainer-build.json, .pnp.cjs, .yarn/cache/@types-node-npm-20.19.30-e3d3d7af6e-4a25e5cbcd.zip and 42 more) — QuantumExplorer or shumkov
    • dashmate (packages/dashmate/docs/installation.md, packages/dashmate/package.json) — ktechmidas or shumkov
    • js-wasm-sdk (packages/js-dash-sdk/README.md, packages/js-dash-sdk/package.json, packages/js-evo-sdk/README.md and 10 more) — shumkov
    • kotlin-sdk (packages/kotlin-sdk/sdk/src/main/kotlin/org/dashfoundation/dashsdk/errors/DashSdkError.kt, packages/kotlin-sdk/sdk/src/test/kotlin/org/dashfoundation/dashsdk/errors/DashSdkErrorTest.kt) — HashEngineering
    • rust-dapi — you own it
    • dpp (packages/rs-dpp/Cargo.toml, packages/rs-dpp/src/address_funds/platform_address.rs, packages/rs-dpp/src/bls/bls_signatures.rs and 25 more) — QuantumExplorer or shumkov
    • rs-drive-abci (packages/rs-drive-abci/src/abci/error.rs, packages/rs-drive-abci/src/error/execution.rs, packages/rs-drive-abci/src/error/mod.rs and 42 more) — QuantumExplorer or shumkov
    • rs-platform-wallet-ffi (packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md, packages/rs-platform-wallet-ffi/src/dashpay.rs, packages/rs-platform-wallet-ffi/src/derivation.rs and 14 more) — HashEngineering or ZocoLini or llbartekll or romchornyi
    • wallet-storage — you own it
    • rs-platform-wallet (packages/rs-platform-wallet/Cargo.toml, packages/rs-platform-wallet/examples/dpns_marketplace_testnet.rs, packages/rs-platform-wallet/src/changeset/core_bridge.rs and 33 more) — HashEngineering or ZocoLini or llbartekll or romchornyi
    • rust-sdk-ffi — you own it
    • rust-sdk — you own it
    • swift-sdk (packages/swift-sdk/Sources/SwiftDashSDK/PlatformWallet/PlatformWalletManagerSPV.swift, packages/swift-sdk/Sources/SwiftDashSDK/PlatformWallet/PlatformWalletResult.swift, packages/swift-sdk/SwiftTests/SwiftDashSDKTests/ErrorHandlingTests.swift and 1 more) — llbartekll or romchornyi

When every merge requirement is met, the PR Hygiene check passes. Reviewer limits do not block merging; other required GitHub checks and protections still apply.

@github-actions github-actions Bot added this to the v5.1.0 milestone Oct 6, 2026
@coderabbitai

coderabbitai Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Repository: dashpay/platform/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 4edaedbb-8260-42a5-b5e5-928922bf0081

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@thepastaclaw

thepastaclaw commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator

✅ Final review complete — no blockers (commit 9774c72)

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Final validation — Phase 1 + Phase 2

Verified the supplied findings against head ad2b8c2, including the exact pinned upstream SPV implementation. Two migration regressions remain: successful SPV startup discards the runtime's client, and legacy WASM-DPP inherits an undeclared WebCrypto runtime requirement; the overlapping historical-storage test suggestions are consolidated below. Verification was static only: the supplied CI snapshot shows Kotlin build/tests passing, principal Rust and JS suites skipped, and PR Hygiene pending.

🔴 2 blocking | 🟡 1 suggestion(s)

Review provenance

Source: reviewer 1: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 2: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: ffi-engineer); reviewer 4: gpt-6.1-sol (agent: phase2-reviewer, role: platform-versioning); reviewer 5: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 6: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); reviewer 7: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 8: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)

  • Triage: critical by gpt-6.1-sol (effort low) — The large cross-cutting diff directly adapts signature and key-handling implementations in packages/rs-dpp/src/identity/identity_public_key/key_type.rs and packages/rs-platform-wallet/src/wallet/provider_key_at_index.rs, alongside historical masternode processing and serialization, rather than merely bumping dependencies.
  • Phase 1 reviewers: muse-spark-1.3-contributor — general (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — rust-quality (completed, effort xhigh); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-3.8-flash-high (antigravity below 15% reserve: weekly 15% left, 5h 100% left), glm-5.3-flash (not used above high effort; tier asks max)
  • Single stage: Phase 1 and Phase 2 reviewed this head side by side, with no blocker gate between them (triage tier)
  • Fresh verifier: gpt-6.1-sol — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — ffi-engineer (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — platform-versioning (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `Cargo.toml`:
- [BLOCKING] Cargo.toml:70: Adapt SpvRuntime to the newly non-blocking SPV run method
  The pinned revision changes DashSpvClient::run() from waiting for shutdown to spawning an internal sync task and returning after startup succeeds. packages/rs-platform-wallet/src/spv/runtime.rs:374–381 still treats that return as shutdown: it removes self.client and clears the peer tracker. Successful startup therefore makes broadcasts fail with "client not started", progress queries return None, and quorum lookups lose access to the running client. SpvRuntime::stop() subsequently finds no client and joins only the completed startup wrapper, so it reports success without stopping the upstream task. That task retains a client clone, storage and event handlers, allowing sync and callbacks to continue after the runtime reports shutdown. Retain the client after successful startup and adapt shutdown tracking to the new upstream task ownership, including the bounded-stop behavior. Add regression coverage for successful startup followed by client queries and explicit stop.

In `packages/rs-dpp/Cargo.toml`:
- [BLOCKING] packages/rs-dpp/Cargo.toml:101-103: Propagate the new Node runtime requirement to legacy WASM-DPP
  The new entropy requirement also reaches @dashevo/wasm-dpp through its DPP dependency. Its ECDSA signByPrivateKey path calls the pinned Core signer, whose secp256k1 0.33 implementation rerandomizes the signing context using rand 0.9 and getrandom 0.3. That backend calls globalThis.crypto.getRandomValues without getrandom 0.2's CommonJS crypto fallback, so ordinary Node 18 scripts without a WebCrypto shim now fail when signing. packages/wasm-dpp/package.json declares no Node minimum, its production loader in lib/index.ts installs no shim, and the legacy dash SDK still documents support for any Node version. The test bootstrap explicitly installs crypto.webcrypto, masking this production failure. Apply the declared Node >=20 migration to the legacy package and update its consumers' advertised requirement, or initialize WebCrypto in the production Node loader to preserve previous support. Cover the production loader without relying on the test bootstrap's shim.

In `packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_list/update_state_masternode_list/v0/mod.rs`:
- [SUGGESTION] packages/rs-drive-abci/src/execution/platform_events/core_based_updates/update_masternode_list/update_state_masternode_list/v0/mod.rs:546-550: Pin pre-migration bytes in the historical storage compatibility test
  Both expected_bytes here and the actual bytes at lines 570–574 are generated with the migrated MasternodeV0 codec and current dependencies. This verifies that extended RPC records project to the same representation as legacy-shaped inputs, but it does not establish compatibility with previously persisted bytes: a shared encoding change would affect both sides and still pass. Preserving the shipped persisted layout is an explicit goal of this migration. Add a fixed fixture produced at base revision 723b09e63610057377ef4efc808476fae43e88ef, deserialize it through deserialize_masternode_entry, and reproduce it byte-for-byte through serialize_masternode_entry for a frozen historical protocol and PlatformVersion::latest(). Those helpers also exercise the versioned Masternode enum used by persistence, which this direct MasternodeV0 encoding test bypasses. No actual persisted-encoding incompatibility was established by this review.

Comment thread Cargo.toml Outdated
Comment thread packages/rs-dpp/Cargo.toml
@lklimek lklimek changed the title chore(platform)!: port rust-dashcore migration from #4932 to v5.1-dev chore(platform)!: update rust-dashcore Oct 6, 2026
@lklimek lklimek changed the title chore(platform)!: update rust-dashcore chore(platform)!: update rust-dashcore and require node >= 20 in wasm Oct 6, 2026
@lklimek lklimek changed the title chore(platform)!: update rust-dashcore and require node >= 20 in wasm chore(platform)!: update rust-dashcore Oct 6, 2026
@lklimek lklimek changed the title chore(platform)!: update rust-dashcore chore(platform)!: update rust-dashcore (incl secp256k1 0.33) Oct 6, 2026
@lklimek
lklimek marked this pull request as ready for review October 6, 2026 15:08
@github-actions

github-actions Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Waiting for bot review — coderabbitai not yet · thepastaclaw requested changes — dismiss the review or push a fix, 2 threads unresolved — resolve them. Wait for the missing reviews, or a writer can post /skip-bots to proceed without them; blocking findings still need addressing.
Full checklist in the description.

@github-actions github-actions Bot added the waiting-bots Waiting for the review bots to report on this head label Oct 6, 2026

@lklimek lklimek left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/self-reviewed

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review — Final validation — Phase 1 + Phase 2

At d9149f1, two integration blockers remain: the dependency update changes SPV lifecycle ownership and introduces a WebCrypto requirement without the corresponding runtime adaptations. The historical-storage suggestion is outdated because its targeted projection test was removed and the retained persistence code matches the updated base. Validation was static only; the supplied CI snapshot still had Rust workspace, JS, Docker, and Swift schema checks queued, with dedicated wallet tests skipped.

🔴 2 blocking

1 finding(s) not shown inline (the lines are not part of this PR's diff)

🔴 Blocking: Adapt SpvRuntime to the newly non-blocking SPV run method
packages/rs-platform-wallet/src/spv/runtime.rs:379-381

At the pinned 314f1060 revision, DashSpvClient::run_locked starts an internally owned task, stores its handle and cancellation token in sync_loop, and returns Ok after startup. This unconditional cleanup therefore removes a successfully running client and clears its peer tracker. Broadcasts and quorum queries subsequently reject the missing client, progress queries return None, and stop() can report success after joining only the completed startup wrapper while upstream synchronization and event monitors remain active.

Shutdown also needs an ownership adaptation, not just client retention. The pinned upstream stop() takes SyncLoop out of its shared Option before awaiting the task, coordinator, network, and storage teardown. The direct timeout around c.stop() at runtime.rs:417 drops that future on expiry, abandoning the remaining teardown; retrying cannot recover it through the runtime's startup-wrapper handle. Retain the client after successful startup and own teardown in a tracked task that survives timeout and caller cancellation, with retries joining the same work and restart blocked until completion. The separately described #5311 fix is absent from this exact head and must land before or together with the migration.

source: muse-spark-1.3-contributor (phase1-reviewer: general, rust-quality); gpt-6.1-sol (phase2-reviewer: general)

1 carried-forward finding(s) already raised on this PR; not re-posting as new inline comments.

Review provenance

Source: reviewer 1: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 2: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: ffi-engineer); reviewer 4: gpt-6.1-sol (agent: phase2-reviewer, role: platform-versioning); reviewer 5: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 6: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); reviewer 7: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 8: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)

  • Triage: critical by gpt-6.1-sol (effort low) — This large cross-cutting migration directly changes cryptographic key generation in packages/rs-dpp/src/identity/identity_public_key/key_type.rs and BLS key validation, private-key serialization, and canonical node-ID derivation in packages/rs-platform-wallet/src/wallet/provider_key_at_index.rs, beyond merely bumping dependencies.
  • Phase 1 reviewers: muse-spark-1.3-contributor — general (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — rust-quality (completed, effort xhigh); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-3.8-flash-high (antigravity below 15% reserve: weekly 15% left, 5h 100% left), glm-5.3-flash (not used above high effort; tier asks max)
  • Single stage: Phase 1 and Phase 2 reviewed this head side by side, with no blocker gate between them (triage tier)
  • Fresh verifier: gpt-6.1-sol — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — ffi-engineer (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — platform-versioning (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/rs-platform-wallet/src/spv/runtime.rs`:
- [BLOCKING] packages/rs-platform-wallet/src/spv/runtime.rs:379-381: Adapt SpvRuntime to the newly non-blocking SPV run method
  At the pinned 314f1060 revision, DashSpvClient::run_locked starts an internally owned task, stores its handle and cancellation token in sync_loop, and returns Ok after startup. This unconditional cleanup therefore removes a successfully running client and clears its peer tracker. Broadcasts and quorum queries subsequently reject the missing client, progress queries return None, and stop() can report success after joining only the completed startup wrapper while upstream synchronization and event monitors remain active.

  Shutdown also needs an ownership adaptation, not just client retention. The pinned upstream stop() takes SyncLoop out of its shared Option before awaiting the task, coordinator, network, and storage teardown. The direct timeout around c.stop() at runtime.rs:417 drops that future on expiry, abandoning the remaining teardown; retrying cannot recover it through the runtime's startup-wrapper handle. Retain the client after successful startup and own teardown in a tracked task that survives timeout and caller cancellation, with retries joining the same work and restart blocked until completion. The separately described #5311 fix is absent from this exact head and must land before or together with the migration.

In `packages/rs-dpp/Cargo.toml`:
- [BLOCKING] packages/rs-dpp/Cargo.toml:102-103: Propagate the new Node runtime requirement to legacy WASM-DPP
  (existing thread: https://github.com/dashpay/platform/pull/5307#discussion_r4195848422)
  Legacy WASM-DPP's ECDSA signByPrivateKey path reaches DPP's signer::sign and the pinned Core recoverable signer. DPP enables secp256k1's rand feature, and secp256k1 0.33.1 rerandomizes its signing context through rand::random(). The getrandom 0.3 wasm_js backend enabled here calls globalThis.crypto.getRandomValues without the previous CommonJS crypto fallback. Node 18 scripts without global WebCrypto therefore fail during signing.

  This head still declares no Node minimum in packages/wasm-dpp/package.json, installs no WebCrypto shim in its production loader, and advertises unrestricted Node support for the legacy SDK. Its test bootstrap explicitly supplies crypto.webcrypto, masking that production condition. The reported Node 22 smoke test validates the proposed supported environment but does not update the compatibility contract shipped here. Land #5310's package requirements, consumer documentation, and runtime alignment before or together with this migration, or preserve older environments through a production-loader shim. Keep production-loader coverage independent of the test bootstrap.

@lklimek

lklimek commented Oct 6, 2026

Copy link
Copy Markdown
Contributor Author

Both @thepastaclaw blockers addressed in separate PRs #5310 and #5311

@github-actions

github-actions Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

📖 Book Preview built successfully.

Download the preview from the workflow artifacts.
To view locally: download the artifact, unzip, and open index.html.

Updated at 2026-10-14T00:43:51.754Z

@github-actions

github-actions Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Waiting for bot review — coderabbitai not yet · thepastaclaw requested changes — dismiss the review or push a fix, 3 threads unresolved — resolve them. Wait for the missing reviews, or a writer can post /skip-bots to proceed without them; blocking findings still need addressing.
Full checklist in the description.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review — Final validation — Phase 1 + Phase 2

At c0b4b97, two blockers are confirmed: the SPV lifecycle wrapper still treats successful startup as termination, and the root-only BLS patch does not protect external Cargo/WASM consumers. The Node runtime finding is fixed, and the historical-storage test finding no longer applies because the criticized test was removed. Verification was static; the supplied CI snapshot has Rust workspace and several JS/E2E checks pending, with the dedicated Rust wallet job skipped.

🔴 2 blocking

1 carried-forward finding(s) already raised on this PR; not re-posting as new inline comments.

Review provenance

Source: reviewer 1: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 2: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: ffi-engineer); reviewer 4: gpt-6.1-sol (agent: phase2-reviewer, role: platform-versioning); reviewer 5: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 6: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); reviewer 7: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 8: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)

  • Triage: critical by gpt-6.1-sol (effort low) — The cross-cutting migration directly changes cryptographic and key-handling code, including RNG seeding in rs-dpp/src/identity/identity_public_key/key_type.rs and BLS key validation and node-ID construction in rs-platform-wallet/src/wallet/provider_key_at_index.rs, rather than merely bumping dependencies.
  • Phase 1 reviewers: muse-spark-1.3-contributor — general (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — rust-quality (completed, effort xhigh); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-3.8-flash-high (antigravity below 15% reserve: weekly 15% left, 5h 100% left), glm-5.3-flash (not used above high effort; tier asks max)
  • Single stage: Phase 1 and Phase 2 reviewed this head side by side, with no blocker gate between them (triage tier)
  • Fresh verifier: gpt-6.1-sol — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — ffi-engineer (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — platform-versioning (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `Cargo.toml`:
- [BLOCKING] Cargo.toml:78-80: Make the WASM BLS compatibility fix reach external Cargo consumers
  Cargo ignores a dependency workspace's [patch] table and does not inherit its lockfile. Consequently, an external workspace consuming dash-sdk or drive-proof-verifier from this Git revision does not receive this compatibility fix. Both reach DPP's bls-signatures feature, which enables blsful → blstrs_plus and dashcore/bls → dashcore-crypto → dash-pkc. blstrs_plus requires blst exactly 0.3.12, and blst's links constraint prevents selecting a second version for dash-pkc. In registry blst 0.3.12, aggregate_verify and fast_aggregate_verify are gated behind std, which its build script excludes on wasm32-unknown-unknown; the pinned dash-pkc calls both unconditionally in pkgs/pkc/src/bls/scheme_ietf.rs:157 and :176. Thus the workspace patch allows in-tree WASM builds while external consumers still encounter missing-method compilation errors. Make aggregate verification compatible in the owning dash-pkc implementation, or encode a compatible dependency in the actual dependency chain, then update the pins while preserving DPP's historical behavior. Moving the patch into another dependency's workspace would not fix external consumption.
- [BLOCKING] Cargo.toml:70: Adapt SpvRuntime to the newly non-blocking SPV run method
  (existing thread: https://github.com/dashpay/platform/pull/5307#discussion_r4195848409)
  At the pinned 314f1060 revision, DashSpvClient::run_locked() spawns the continuous sync task, stores its SyncLoop internally, and returns Ok after startup. SpvRuntime::run() still unconditionally removes self.client and clears the peer tracker at runtime.rs:379–381 when that call returns. Successful startup therefore disables broadcasts, progress queries, and quorum lookups; stop() then finds no client and reports success after joining only the completed startup wrapper, while upstream synchronization and callbacks can remain active. Shutdown also needs cancellation-safe ownership: upstream stop() takes the internal SyncLoop before awaiting its task, coordinator, network, and storage cleanup, but the current runtime drops that future on timeout. Retaining the client alone would leave teardown completion untracked after timeout or caller cancellation. Incorporate #5311's lifecycle adaptation with this pin, preserving a tracked teardown task and restart barrier until cleanup completes, and cover post-start queries, explicit shutdown, and timeout/cancellation retries.

Comment thread Cargo.toml Outdated
@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Waiting for bot review — coderabbitai not yet · thepastaclaw requested changes — dismiss the review or push a fix, 2 threads unresolved — resolve them. Wait for the missing reviews, or a writer can post /skip-bots to proceed without them; blocking findings still need addressing.
Full checklist in the description.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review — Final validation — Phase 1 + Phase 2

Verified the findings against head 938b60f and the exact pinned upstream sources; two blockers remain after consolidating the duplicate SPV reports. The Node runtime requirement is satisfied, and the historical projection-test suggestion no longer applies. This was static verification only: the supplied CI snapshot shows successful JS/Docker builds and Kotlin checks, with Rust workspace tests running and JS/runtime suites queued.

🔴 2 blocking

2 carried-forward finding(s) already raised on this PR; not re-posting as new inline comments.

Review provenance

Source: reviewer 1: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 2: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: ffi-engineer); reviewer 4: gpt-6.1-sol (agent: phase2-reviewer, role: platform-versioning); reviewer 5: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 6: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); reviewer 7: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 8: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)

  • Triage: critical by gpt-6.1-sol (effort low) — This cross-cutting migration directly changes cryptographic key generation and handling, including RNG seeding in packages/rs-dpp/src/identity/identity_public_key/key_type.rs and provider key derivation in packages/rs-platform-wallet/src/wallet/provider_key_at_index.rs, rather than merely updating dependency pins.
  • Phase 1 reviewers: muse-spark-1.3-contributor — general (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — rust-quality (completed, effort xhigh); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-3.8-flash-high (antigravity below 15% reserve: weekly 15% left, 5h 100% left), glm-5.3-flash (not used above high effort; tier asks max)
  • Single stage: Phase 1 and Phase 2 reviewed this head side by side, with no blocker gate between them (triage tier)
  • Fresh verifier: gpt-6.1-sol — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — ffi-engineer (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — platform-versioning (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `Cargo.toml`:
- [BLOCKING] Cargo.toml:78-80: Make the WASM BLS compatibility fix reach external Cargo consumers
  (existing thread: https://github.com/dashpay/platform/pull/5307#discussion_r4198966217)
  This patch makes repository builds work, but Cargo does not inherit a dependency workspace's root [patch.crates-io] table. An external wasm32-unknown-unknown consumer enabling DPP's bls-signatures feature brings in blsful → blstrs_plus, whose manifest requires exactly blst =0.3.12. The migrated dashcore BLS feature also enables dash-pkc at e6402ced, whose scheme_ietf.rs calls fast_aggregate_verify and aggregate_verify unconditionally. Registry blst 0.3.12 gates both methods behind std, and its build script deliberately does not enable that configuration for this target. Consequently, the external dependency graph lacks the methods required by the migrated implementation, even though the workspace's patched builds pass.

  The reported registry-versus-patched reproduction demonstrates why the workaround is necessary, not that it reaches downstream Cargo roots. Make compatibility effective through the normal dependency chain, or explicitly document the required consumer-root patch and its exact revision in the supported integration instructions. Validate that configuration from a separate Cargo root. Existing prebuilt npm artifacts are not affected by this dependency-resolution issue.
- [BLOCKING] Cargo.toml:70: Adapt SpvRuntime to the newly non-blocking SPV run method
  (existing thread: https://github.com/dashpay/platform/pull/5307#discussion_r4195848409)
  At this pin, DashSpvClient::run_locked spawns the synchronization task, stores its handle internally, and returns Ok(()) after startup. SpvRuntime::run still treats that return as termination: runtime.rs:379–381 removes self.client and clears the peer tracker. Successful startup therefore removes access to broadcasts, progress and quorum queries while upstream synchronization continues. A subsequent stop finds no client and joins only the completed startup wrapper; PlatformWalletManager::shutdown then records WorkerStatus::Ok without stopping the upstream task. That task retains the client and event handlers, so synchronization and callbacks can remain active after reported shutdown.

  Client retention alone is insufficient: runtime.rs:417 directly times out c.stop(), while the pinned upstream stop takes its internal SyncLoop before awaiting teardown. Cancelling that future can discard shutdown ownership, and Platform's outer startup handle cannot establish that the internal task and remaining cleanup completed. Incorporate the #5311 lifecycle adaptation with this migration: retain the client after successful startup, preserve a tracked teardown task across timeout and caller cancellation, let retries join the same work, and prevent restart until teardown completes. The separately reported #5311 tests do not validate this head, whose runtime has no corresponding lifecycle change.

@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Waiting for bot review — coderabbitai not yet · thepastaclaw requested changes — dismiss the review or push a fix, 2 threads unresolved — resolve them. Wait for the missing reviews, or a writer can post /skip-bots to proceed without them; blocking findings still need addressing.
Full checklist in the description.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review — Final validation — Phase 1 + Phase 2

The inspected cryptographic adaptations preserve signing preimages and canonical byte handling, and the incorporated Node requirements and SPV teardown changes resolve the earlier lifecycle blockers. Two blocking defects remain: external Cargo WASM consumers do not inherit the BLS compatibility patch, and clearing a constructed-but-not-running SPV client can leave its persistence worker writing to the cleared directory. This was a static review at the exact head; the supplied CI snapshot shows Kotlin checks passing, with Rust workspace validation, JS builds, Swift schema validation, and Docker builds still pending.

🔴 2 blocking | 🟡 2 suggestion(s)

1 carried-forward finding(s) already raised on this PR; not re-posting as new inline comments.

Review provenance

Source: reviewer 1: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 2: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: ffi-engineer); reviewer 4: gpt-6.1-sol (agent: phase2-reviewer, role: platform-versioning); reviewer 5: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 6: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); reviewer 7: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 8: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); reviewer 9: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 10: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 11: gpt-6.1-sol (agent: phase2-reviewer, role: ffi-engineer); reviewer 12: gpt-6.1-sol (agent: phase2-reviewer, role: platform-versioning); reviewer 13: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 14: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)

  • Triage: critical by gpt-6.1-sol (effort low) — The large, cross-cutting diff directly adapts cryptographic and key-handling implementations in packages/rs-dpp/src/identity/identity_public_key/key_type.rs, packages/rs-platform-encryption/src/ecdh.rs, and wallet signing and key-derivation code, beyond merely bumping dependencies.
  • Phase 1 reviewers: muse-spark-1.3-contributor — general (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — rust-quality (completed, effort xhigh); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-3.8-flash-high (antigravity below 15% reserve: weekly 15% left, 5h 100% left), glm-5.3-flash (not used above high effort; tier asks max)
  • Single stage: Phase 1 and Phase 2 reviewed this head side by side, with no blocker gate between them (triage tier)
  • Fresh final gate: an independent Phase-2 review ran after iterative findings were reconciled
  • Fresh verifier: gpt-6.1-sol — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — ffi-engineer (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — platform-versioning (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — ffi-engineer (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — platform-versioning (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/rs-platform-wallet/src/spv/runtime.rs`:
- [BLOCKING] packages/rs-platform-wallet/src/spv/runtime.rs:620-623: Stop the constructor's persistence worker before clearing storage
  A supported start(config) followed by clear_storage() without spawning the run loop leaves the original storage worker alive. At the pinned rust-dashcore revision 314f1060, DiskStorageManager::new() immediately starts a five-second persistence worker, but DashSpvClient::stop() stops storage only when sync_loop contains a running loop. This closure consequently returns without stopping that worker, and stop_locked drops the client before clear_storage_with reopens the same directory. DiskStorageManager has no Drop cleanup for its worker handle; the detached task retains the old storage Arcs while the directory lock is released. Its persistence loop continues creating storage subdirectories and can write still-dirty header segments into the cleared or restarted store. The previous live-client clear path cleared through the existing storage manager, which explicitly cancelled its worker. Stop the client's storage explicitly within the owned cleanup before dropping it, including cleanup after failed startup, and add a constructor-only clear regression that verifies the old worker cannot write after clearing.
- [SUGGESTION] packages/rs-platform-wallet/src/spv/runtime.rs:299-304: Separate retained client ownership from background running state
  Retaining the client is necessary for safe teardown, but its presence no longer proves that synchronization is running. The pinned upstream run() returns after startup; a later sync-loop failure invokes on_error and schedules stop_failed(), without returning another error to this wrapper. finish_startup therefore leaves self.client populated after the upstream client has stopped. SpvRuntime::is_started() tests only that Option, and platform_wallet_manager_spv_is_running exposes the result as whether SPV is currently running. PlatformEventManager forwards the error without updating lifecycle state, so that query can remain true indefinitely after background synchronization terminates. Keep resource ownership separate from running status, using the upstream is_running() query or an explicit lifecycle signal, and cover successful startup followed by a background failure.
- [SUGGESTION] packages/rs-platform-wallet/src/spv/runtime.rs:999-1000: Add a regression that runs the real SPV startup wrapper
  This is useful unit coverage for finish_startup, but it passes Ok(()) directly and installs an unrelated empty Tokio task. It never executes SpvRuntime::run() or creates the upstream SyncLoop, so it cannot detect a regression in the production startup-task wiring, and its stop assertion does not exercise running-client teardown. Add a separate regression that calls spawn_run_loop(), waits for the actual startup task to finish, then checks retained query access and explicit stop. The existing offline configuration restricts connections to an empty configured peer list; the pinned network manager supports that configuration without DNS discovery or peer connections, so this can exercise real startup without network-dependent testing.

In `Cargo.toml`:
- [BLOCKING] Cargo.toml:78-80: Make the WASM BLS compatibility fix reach external Cargo consumers
  (existing thread: https://github.com/dashpay/platform/pull/5307#discussion_r4198966217)
  Cargo applies this patch only when Platform is the consuming workspace root; an independent application depending on DPP or dash-sdk does not inherit it or Platform's lockfile. The ordinary dependency graph still combines blsful → blstrs_plus 0.8.18, which requires exactly registry blst 0.3.12, with dashcore → dashcore-crypto → dash-pkc. At the pinned base-sdk revision e6402ced, dash-pkc's scheme_ietf.rs unconditionally calls fast_aggregate_verify and aggregate_verify. Registry blst 0.3.12 gates both methods behind std, and its build script excludes that configuration on wasm32-unknown-unknown. External WASM consumers enabling BLS therefore encounter missing-method compilation errors, even though repository builds use the compatible patch. The reported patched reproducer establishes the implementation fix, but not its delivery to downstream consumers. Make the repair available through ordinary transitive dependency declarations, or explicitly document and support the required consumer-root override, and validate a standalone WASM consumer outside this workspace. This affects Rust-to-WASM consumers, not users of the already-built npm artifacts.

Comment thread packages/rs-platform-wallet/src/spv/runtime.rs
Comment thread packages/rs-platform-wallet/src/spv/runtime.rs
Comment thread packages/rs-platform-wallet/src/spv/runtime.rs
@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Waiting for bot review — coderabbitai not yet · thepastaclaw requested changes — dismiss the review or push a fix, 1 thread unresolved — resolve it. Wait for the missing reviews, or a writer can post /skip-bots to proceed without them; blocking findings still need addressing.
Full checklist in the description.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review — Final validation — Phase 1 + Phase 2

The current head incorporates the Node-runtime, retained-client shutdown, and external-consumer BLS fixes, but one blocking SPV storage-worker defect and two lifecycle/testing suggestions remain. The storage-clear path can reopen the directory while its original persistence worker is still alive. Validation was static only; the supplied CI snapshot shows successful JS builds and Kotlin checks, with Rust workspace tests, Docker builds, and most JS checks pending and Rust wallet tests skipped.

🔴 1 blocking | 🟡 2 suggestion(s)

3 carried-forward finding(s) already raised on this PR; not re-posting as new inline comments.

Review provenance

Source: reviewer 1: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 2: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: ffi-engineer); reviewer 4: gpt-6.1-sol (agent: phase2-reviewer, role: platform-versioning); reviewer 5: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 6: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); reviewer 7: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 8: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)

  • Triage: critical by gpt-6.1-sol (effort low) — The large cross-cutting diff directly changes cryptographic key parsing, signature handling and canonical encodings in packages/rs-dpp/src/bls/bls_signatures.rs and packages/rs-dpp/src/bls/serde.rs, rather than merely updating dependency pins.
  • Phase 1 reviewers: muse-spark-1.3-contributor — general (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — rust-quality (completed, effort xhigh); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-3.8-flash-high (antigravity below 15% reserve: weekly 15% left, 5h 100% left), glm-5.3-flash (not used above high effort; tier asks max)
  • Single stage: Phase 1 and Phase 2 reviewed this head side by side, with no blocker gate between them (triage tier)
  • Fresh verifier: gpt-6.1-sol — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — ffi-engineer (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — platform-versioning (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/rs-platform-wallet/src/spv/runtime.rs`:
- [BLOCKING] packages/rs-platform-wallet/src/spv/runtime.rs:620-623: Stop the constructor's persistence worker before clearing storage
  (existing thread: https://github.com/dashpay/platform/pull/5307#discussion_r4205797515)
  This cleanup stops only the upstream client before dropping it and reopening its storage directory. At the exact rust-dashcore pin, DiskStorageManager::new() immediately starts a five-second persistence task, but DashSpvClient::stop() reaches storage.stop() only when a SyncLoop exists. Therefore start(config) followed by clear_storage() without spawn_run_loop() releases the original directory lock without stopping its writer. The detached task retains the old storage components and path; persistence recreates directories and writes dirty segments, so clearing or restarting can race with stale writes. This PR replaces the base's live-client clear path, which cleared through the existing storage manager and cancelled that worker. Explicitly stop the original storage within owned teardown before releasing or reopening the directory, and ensure construction/startup failure cleanup cannot detach the same worker. Add a constructor-only clear regression that checks for writes after multiple persistence periods. The cleanup described in your #5322 reply is not present here: runtime.rs is unchanged since 7c73e983.
- [SUGGESTION] packages/rs-platform-wallet/src/spv/runtime.rs:186-188: Separate retained client ownership from background running state
  (existing thread: https://github.com/dashpay/platform/pull/5307#discussion_r4205797527)
  is_started() measures retained client ownership, but platform_wallet_manager_spv_is_running exposes it as synchronization liveness. The pinned upstream run() returns after startup; a later background failure cancels its running token, reports on_error, and schedules stop_failed() without returning through this runtime's completed startup wrapper. PlatformEventManager only forwards the error, so the retained client remains populated and the public running query can stay true after synchronization stops. Keep the client for teardown, but expose a separate running query backed by upstream is_running() and route the FFI export through it. Your real channel-failure regression tests the appropriate boundary, but neither that regression nor the separate query from #5322 appears in this head.
- [SUGGESTION] packages/rs-platform-wallet/src/spv/runtime.rs:999-1000: Add a regression that runs the real SPV startup wrapper
  (existing thread: https://github.com/dashpay/platform/pull/5307#discussion_r4205797537)
  This regression calls finish_startup(Ok(())) directly and installs an unrelated empty Tokio task. offline_runtime() constructs the client but never starts its upstream SyncLoop, so the assertions test constructor-level query access and helper behavior rather than production startup wiring or running-client teardown. The other spawn_run_loop() calls in this module exercise teardown refusal or an unstarted runtime, not successful startup. Add a separate offline regression that calls spawn_run_loop(), waits for its actual startup task, checks retained query access, explicitly stops the running client, and verifies restart. The existing empty restricted-peer configuration supports this without DNS discovery or peer connections. The production-wrapper regression linked in your #5322 reply has not been incorporated into the reviewed tree.

lklimek and others added 2 commits October 13, 2026 15:10
Move the eight rust-dashcore workspace pins from the head of
rust-dashcore#1149 (now merged) to `dev` at d036d844, and the `dash-pkc`
base-sdk pin to 33fcc59e so the lockfile keeps one base-sdk revision.

Adapt to what changed upstream in between:

- `signer::CompactSignature` is gone. Address-witness verification and
  the Core-signer paths use `sign_message::MessageSignature`. It accepts
  the same 65-byte signatures (header 27..=34, same recovery-id mapping)
  and `MessageSignature::new(sig, true).serialize()` yields the bytes
  `to_compact_signature(true)` did; only the parse error text differs.
- `dashcore_rpc::Error::Secp256k1` is gone; drop its DAPI mapping.
- ProRegTx / ProUpServTx carry `MasternodeNetInfo`. The wallet reads the
  primary routable address and keeps the `ip:port` rendering; version-3
  platform ports and payout shares are not surfaced yet (TODO).
- `WalletInfoInterface::truncate_above` is delegated to the Core wallet.
- `WalletEvent::ChainTruncated` updates the displayed balance. It is not
  mirrored to the persister: the changeset cannot lower a height, sweep
  without a winner, or demote a confirmed record. Marked TODO(#5380).

Document the removed BIP38 C exports in the changelog.

<sub>🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent</sub>

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSi8cX229kW2fmLxKW6ir4
dash-spv rewinds its storage and the wallets to a fork a peer announces
before the competing branch is validated beyond its own header; only the
best ChainLock bounds it. The fix belongs upstream and is tracked in
dashpay/rust-dashcore#1103, so leave a TODO where the client is built.

<sub>🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent</sub>

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSi8cX229kW2fmLxKW6ir4
@PastaPastaPasta
PastaPastaPasta force-pushed the chore/rust-dashcore-1112-v5.1 branch from 5d8438f to 1bc426d Compare October 13, 2026 20:10
@PastaPastaPasta

Copy link
Copy Markdown
Member

I force-pushed this branch to reorganize its history: 5d8438f695 → 1bc426d228. The content is unchanged; git diff 5d8438f695 1bc426d228 is empty. The goal is to make it clear what this PR adds on top of the sub-PRs that were already reviewed and merged into it.

New layout (on 6499c680c6, the v5.1-dev commit the sub-PRs were built on):

  1. chore(platform)!: port rust-dashcore migration (secp256k1 0.33) to v5.1: a single commit for the chore(platform)!: bump rust-dashcore to 719de34b (secp256k1 0.33) #4932 port. It replaces the port merge, the two earlier v5.1-dev merges, and the interim Node 20 change that chore!: require Node.js >=22 for JS packages and >=24 for dashmate #5310 superseded.
  2. The other 16 commits, replayed in their original order: the sub-PRs chore!: require Node.js >=22 for JS packages and >=24 for dashmate #5310, fix(platform-wallet): retain SPV client and track shutdown through cancellation #5311, refactor: share rust-dashcore BLS backend through dpp::bls #5320, fix(platform-wallet)!: tell a retryable SPV stop from one that needs a restart #5322 and feat(platform-wallet): return dash-spv's verification status with an SPV quorum key #5366, plus every other commit made directly on this branch. Each one's diff (hunk headers and blob hashes included), message and author are byte-identical to the original.
  3. One Merge branch 'v5.1-dev' bringing in test(platform): freeze historical storage and signing vectors #5359, followed by build!: pin rust-dashcore to dev d036d844 and the fork-rewind TODO.

Why there's still one merge: #5359 upstreamed this branch's frozen vectors and part of #5320's BLS compatibility tests. With #5359 underneath, #5320's reviewed diff no longer applies unchanged, so v5.1-dev has to be merged after the sub-PRs. git show --remerge-diff on the merge shows its resolution.

What's left to review beyond the sub-PRs: commit 1, the merge resolution, and the branch's own commits in the map below.

old new
d9149f1ab5 1aba0bd56d chore(platform): pin rust-dashcore to merged dev
c0b4b97f8a 6e022c0dc8 chore!: require Node.js >=22 for JS packages and >=24 for dashmate (#5310)
7c73e983b4 70c02aff24 fix(platform-wallet): retain SPV client and track shutdown through cancellation (#5311)
4045c13fda cdbeec5672 refactor: share rust-dashcore BLS backend through dpp::bls (#5320)
8377b64f31 80ee8f563c fix(platform-wallet)!: tell a retryable SPV stop from one that needs a restart (#5322)
9af203336c fc62159191 chore(deps)!: pin rust-dashcore to BLS compatibility fix
41e945377d 1b804dd6e2 chore(deps): pin rust-dashcore to legacy serde restoration
1dbcaaf70b 007a51f542 fix(drive-abci): preserve quorum key storage independently of serde
d8b911d9ca b7a246ce23 fix(wallet-ffi): adapt platform node ID hashing to rust-dashcore
c1c7b095c2 835f6af4bf fix(test-suite): wait for rotated quorum publication before retrying proofs
069a5cab6a 0a4ade5bfc fix(dpp): require WASM-compatible blst for external consumers
cbb694d261 00b1c6c0cd test(platform): freeze historical storage and signing vectors
f475f72a11 dd29a05b6f chore(deps): pin rust-dashcore to PR 1149
7ee8f7b1a8 97baf6b740 feat(platform-wallet): return dash-spv's verification status with an SPV quorum key (#5366)
9183842643 50b28def80 build!: pin rust-dashcore to dev d036d844
5d8438f695 1bc426d228 docs(platform-wallet): mark the unvalidated fork rewind in dash-spv

Open PRs based on this branch (#5150, #5323, #4844) need a rebase onto 1bc426d228. A plain git rebase --onto 1bc426d228 5d8438f695 works, because the tree is identical.


🤖 Posted autonomously by Claude on behalf of pasta.

@github-actions

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Waiting for bot review — coderabbitai not yet · thepastaclaw not yet. Wait for the missing reviews, or a writer can post /skip-bots to proceed without them; blocking findings still need addressing.
Full checklist in the description.

@github-actions github-actions Bot added waiting-bots Waiting for the review bots to report on this head and removed waiting-self-review Waiting for the author to post /self-reviewed bot-review-skipped A required review bot did not report; it was skipped by the window or by a person. labels Oct 13, 2026
lklimek and others added 4 commits October 13, 2026 21:18
Moves the pin one commit forward on rust-dashcore `dev`, from d036d844 to
its direct child 90359ed5, which adds rust-dashcore#1155: a special
transaction payload is decoded only from transaction version 3, as Core
does. Below version 3, non-zero type bytes stay raw and carry no payload.

v5.1-dev already relies on that decoding through its own pin, so the
branch needs it before the base can be merged without losing behaviour.
Cargo.lock changes only the rust-dashcore source revision.

<sub>🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent</sub>

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSi8cX229kW2fmLxKW6ir4
`dpp::bls::PublicKey` parses the canonical identity encoding, as persisted
state and shipped validation require, and only refuses it when a signature
is verified. A caller that must not accept such a key at all, like a
quorum key taken from a trusted source, had no way to ask.

<sub>🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent</sub>

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSi8cX229kW2fmLxKW6ir4
Brings in v5.1-dev at 1188304, which decodes Core v24 blocks and
transactions (#5392) against rust-dashcore 6c5775cf. This branch stays on
rust-dashcore `dev` at 90359ed5, which carries an equivalent of every
commit 6c5775cf has over the common ancestor.

Conflicts:
- Cargo.toml, Cargo.lock: grovedb from v5.1-dev, rust-dashcore from this
  branch. The lock starts from v5.1-dev's and re-resolves only the
  rust-dashcore source; no other package takes a version that neither
  parent had.
- CHANGELOG.md: both sides kept. The note that version-3 provider
  payloads are read through their pre-v3 fields is dropped, v5.1-dev
  reads them.
- rs-drive-abci rpc/core.rs: `credit_pool_balance_from_coinbase` is the
  same on both sides; v5.1-dev's comment and tests are kept, they cover
  more.
- rs-platform-wallet masternode record.rs, tracked.rs: v5.1-dev's
  version-3 handling (payout list, platform HTTP port from the address
  map) replaces this branch's pre-v3-only reading.
- rs-platform-wallet masternode update_service.rs: this branch's
  `dpp::bls` imports.

Ports the merge forces, because v5.1-dev added callers of the generic
`dpp::bls_signatures` API this branch removed:
- rs-sdk-trusted-context-provider: a quorum key from the trusted source
  is parsed with `dpp::bls::PublicKey`, which accepts the identity point,
  so it is refused through `is_identity()`; a test covers that key.
- Tests in rs-drive-abci, rs-drive-proof-verifier and wasm-sdk sign with
  `dpp::bls::SecretKey`. Keys that committed vectors depend on are the
  scalars the previous backend derived from the same seeds, so the
  vectors are unchanged.

<sub>🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent</sub>

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSi8cX229kW2fmLxKW6ir4
…allet

dash-spv tells the wallet the height of a chain lock it sees during the
initial sync, before any chain lock is applied, so that spends observed
at or below the synced height can be forgotten. `PlatformWalletInfo`
forwarded `apply_chain_lock` but not `note_chain_lock_height`, whose
trait default does nothing, so those spends stayed until the first chain
lock was applied.

<sub>🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent</sub>

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSi8cX229kW2fmLxKW6ir4

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review — Final validation — Phase 1 + Phase 2

The dependency, cryptography, persistence, and SPV lifecycle adaptations are present at the reviewed head, and no in-scope blocking defect remains. Four non-blocking wallet integration issues remain: initial-sync ChainLock notifications are not forwarded, native IPv6 endpoints are rendered ambiguously, v3 service updates can erase previously known endpoint fields, and quorum-type inputs are silently narrowed. Several broader persistence and upstream lifecycle limitations remain explicitly deferred and are recorded as follow-ups.

🟡 4 suggestion(s)

1 finding(s) not shown inline (the lines are not part of this PR's diff)

🟡 Suggestion: Reject quorum types before narrowing them to u8
packages/rs-platform-wallet/src/spv/runtime.rs:371-375

The public SPV lookup accepts a u32 quorum type but casts it directly to u8. Inputs such as 256 and 257 therefore wrap to 0 and 1 and can query a different LLMQ type instead of returning an invalid-input error. This boundary is used by FFI and proof-verification integrations, so silently aliasing malformed input is unsafe and nondeterministic from the caller's perspective. Use a checked conversion before constructing LLMQType.

        let quorum_type = u8::try_from(quorum_type).map_err(|_| {
            PlatformWalletError::SpvError(format!("invalid quorum type: {quorum_type}"))
        })?;
        let llmq_type = LLMQType::from(quorum_type);

source: muse-spark-1.3-contributor (phase1-reviewer: general, rust-quality); gpt-6.1-sol (phase2-reviewer: general, architecture-layering, ffi-engineer, platform-versioning, rust-quality, security-auditor)

Review provenance

Source: reviewer 1: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 2: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: ffi-engineer); reviewer 4: gpt-6.1-sol (agent: phase2-reviewer, role: platform-versioning); reviewer 5: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 6: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 7: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); reviewer 8: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); reviewer 9: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 10: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 11: gpt-6.1-sol (agent: phase2-reviewer, role: ffi-engineer); reviewer 12: gpt-6.1-sol (agent: phase2-reviewer, role: platform-versioning); reviewer 13: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 14: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)

  • Triage: critical by gpt-6.1-sol (effort low) — This is a large cross-cutting dependency migration that directly changes cryptography and signature/key handling, consensus-facing serialization and peer-facing network data parsing across Rust, WASM, wallet, DPP, and FFI code, including files such as packages/rs-dpp/src/bls/, packages/rs-platform-wallet/src/masternode/, and packages/rs-platform-wallet/src/spv/runtime.rs.
  • Phase 1 reviewers: muse-spark-1.3-contributor — general (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — rust-quality (completed, effort xhigh); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-3.8-flash-high (antigravity below 15% reserve: weekly 14% left, 5h 100% left), glm-5.3-flash (not used above high effort; tier asks max)
  • Single stage: Phase 1 and Phase 2 reviewed this head side by side, with no blocker gate between them (triage tier)
  • Fresh final gate: an independent Phase-2 review ran after iterative findings were reconciled
  • Fresh verifier: gpt-6.1-sol — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — ffi-engineer (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — platform-versioning (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — ffi-engineer (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — platform-versioning (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/rs-platform-wallet/src/wallet/platform_wallet_traits.rs`:
- [SUGGESTION] packages/rs-platform-wallet/src/wallet/platform_wallet_traits.rs:164-166: Forward initial-sync ChainLock height notifications to the inner wallet
  The pinned WalletInfoInterface adds note_chain_lock_height with a no-op default. dash-spv uses this callback for validated ChainLocks buffered during initial synchronization, but PlatformWalletInfo delegates truncate_above and the other ChainLock methods without forwarding it. The inner ManagedWalletInfo therefore does not receive the observed finality boundary until SyncComplete, so observed-spend entries are retained longer than necessary during a long initial sync. Delegate the callback and cover the pruning behavior through this wrapper.

In `packages/rs-platform-wallet/src/masternode/record.rs`:
- [SUGGESTION] packages/rs-platform-wallet/src/masternode/record.rs:28-36: Preserve bracketed rendering for native IPv6 provider addresses
  provider_ip_port normalizes IPv4-mapped IPv6 addresses, but then formats the remaining address with format!("{}:{}"). A native IPv6 endpoint is consequently rendered as an ambiguous value such as 2001:db8::1:20001, unlike the ProRegTx path, which uses SocketAddr::to_string() and emits [2001:db8::1]:20001. Consumers parsing the documented endpoint string cannot reliably separate the host and port. Render the normalized address as a SocketAddr instead.
- [SUGGESTION] packages/rs-platform-wallet/src/masternode/record.rs:377-384: Do not erase previously exposed masternode fields on v3 service updates
  When a v3 ProUpServTx is the latest service update, the current code unconditionally assigns service_address from primary_service_address() and platform_http_port from the legacy field. A v3 payload may have no legacy primary IPv4/IPv6 entry and may carry its platform port only in the extended address map, so both expressions can produce None. The update then clears endpoint data that was known from an earlier registration or service update, while advancing service_height. Preserve each existing field unless the update supplies a usable replacement, or extract the corresponding v3 extended-map values.

In `packages/rs-platform-wallet/src/spv/runtime.rs`:
- [SUGGESTION] packages/rs-platform-wallet/src/spv/runtime.rs:371-375: Reject quorum types before narrowing them to u8
  The public SPV lookup accepts a u32 quorum type but casts it directly to u8. Inputs such as 256 and 257 therefore wrap to 0 and 1 and can query a different LLMQ type instead of returning an invalid-input error. This boundary is used by FFI and proof-verification integrations, so silently aliasing malformed input is unsafe and nondeterministic from the caller's perspective. Use a checked conversion before constructing LLMQType.
Out-of-scope follow-up suggestions (4)

These are valid observations, but they are outside this PR's scope and should be handled in separate issues or author/maintainer-requested PRs rather than blocking this review.

  • Persist the rollback represented by ChainTruncated — The ChainTruncated adapter still emits an empty CoreChangeSet, so durable transaction rows, restored outpoints, and sync heights can remain on the replaced branch after restart. This is a concrete durability gap, but the PR explicitly documents it and defers the ordered changeset and SQLite, SwiftData, and Room work to #5380/#5384.
    • Follow-up: Land the shared truncation operation and truncate-to-persist-to-reload coverage before releasing a wallet build that relies on SPV fork recovery.
  • Validate the competing branch before destructive SPV rewind — The pinned upstream dash-spv implementation can emit fork recovery before fully validating the competing branch, allowing a peer to trigger bounded wallet and header rewinds. The limitation is upstream, explicitly documented in this PR, and tracked as rust-dashcore#1103 rather than introduced by a Platform-side adaptation.
    • Follow-up: Pin the upstream fork-recovery fix and add a regression proving invalid or insufficient-work competing headers cannot trigger a destructive rewind.
  • Join upstream auxiliary SPV tasks before declaring teardown complete — dash-spv can leave detached stop_failed or fork-recovery helpers holding client and storage references after the tracked synchronization task and client stop complete. The resulting transient storage-lock window is acknowledged in the PR and tracked as #5381; the private helper tasks cannot be joined by this downstream adapter alone.
    • Follow-up: Add an upstream completion barrier that joins all helper tasks before SPV stop reports success, with immediate clear and restart coverage.
  • Stamp the v4 coinbase blob format in the SQLite schema — Version-4 coinbase records add a serialized field while this branch retains schema V018, so a build containing this migration but not stacked #5150 does not reject the new format through the schema gate. The PR explicitly identifies V019 from #5150 as the required follow-up and states that the two changes must land together.
    • Follow-up: Land #5150's V019 migration and older-reader rejection before deploying this dependency update to persistent wallet databases.

Comment thread packages/rs-platform-wallet/src/wallet/platform_wallet_traits.rs
Comment thread packages/rs-platform-wallet/src/masternode/record.rs Outdated
Comment thread packages/rs-platform-wallet/src/masternode/record.rs

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review — Final validation — Phase 1 + Phase 2

The pinned dash-spv revision introduces a remotely triggerable fork-recovery path that can truncate wallet and chain state before the competing branch is validated; the local TODO documents the risk but does not mitigate it. The lifecycle adaptation also allows readiness to remain true after background synchronization stops because the retained client and cached peer snapshot are treated as sufficient. Other reviewed concerns are either fixed or explicitly deferred to tracked follow-up work.

🔴 1 blocking | 🟡 1 suggestion(s)

1 finding(s) not shown inline (the lines are not part of this PR's diff)

🟡 Suggestion: Gate broadcast readiness on the retained client's running state
packages/rs-platform-wallet/src/spv/runtime.rs:271-272

The new lifecycle intentionally retains self.client after upstream synchronization fails, while is_running() becomes false. is_broadcast_ready() currently treats the retained client and the last cached PeerTracker snapshot as sufficient, and PeerTracker is only cleared during owned teardown. After a background failure, a stale nonempty peer snapshot can therefore make wait_until_ready() return true even though the upstream client has stopped; a subsequent broadcast is then rejected or cannot reach the network. Query the retained client's running state before reporting readiness, and apply the same guard to connected_peers().

    async fn is_broadcast_ready(&self) -> bool {
        let client_guard = self.client.read().await;
        let Some(client) = client_guard.as_ref() else {
            return false;
        };
        client.is_running() && !self.peer_tracker.snapshot().is_empty()
    }

source: muse-spark-1.3-contributor (phase1-reviewer: general, rust-quality)

1 carried-forward finding(s) already raised on this PR; not re-posting as new inline comments.

Review provenance

Source: reviewer 1: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 2: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: ffi-engineer); reviewer 4: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 5: gpt-6.1-sol (agent: phase2-reviewer, role: platform-versioning); reviewer 6: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 7: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); reviewer 8: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); reviewer 9: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 10: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 11: gpt-6.1-sol (agent: phase2-reviewer, role: ffi-engineer); reviewer 12: gpt-6.1-sol (agent: phase2-reviewer, role: platform-versioning); reviewer 13: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 14: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)

  • Triage: critical by gpt-6.1-sol (effort low) — This large cross-cutting diff changes cryptographic APIs, BLS and ECDSA signing/key handling, legacy public-key serialization, consensus-facing transaction payload decoding, proof verification, and wallet funds/key-management paths, including packages/rs-dpp/src/bls and packages/rs-platform-wallet/src/spv/runtime.rs.
  • Phase 1 reviewers: muse-spark-1.3-contributor — general (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — rust-quality (completed, effort xhigh); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-3.8-flash-high (antigravity below 15% reserve: weekly 14% left, 5h 100% left), glm-5.3-flash (not used above high effort; tier asks max)
  • Single stage: Phase 1 and Phase 2 reviewed this head side by side, with no blocker gate between them (triage tier)
  • Fresh final gate: an independent Phase-2 review ran after iterative findings were reconciled
  • Fresh verifier: gpt-6.1-sol — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — ffi-engineer (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — platform-versioning (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — ffi-engineer (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — platform-versioning (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/rs-platform-wallet/src/spv/runtime.rs`:
- [SUGGESTION] packages/rs-platform-wallet/src/spv/runtime.rs:271-272: Gate broadcast readiness on the retained client's running state
  The new lifecycle intentionally retains self.client after upstream synchronization fails, while is_running() becomes false. is_broadcast_ready() currently treats the retained client and the last cached PeerTracker snapshot as sufficient, and PeerTracker is only cleared during owned teardown. After a background failure, a stale nonempty peer snapshot can therefore make wait_until_ready() return true even though the upstream client has stopped; a subsequent broadcast is then rejected or cannot reach the network. Query the retained client's running state before reporting readiness, and apply the same guard to connected_peers().

In `Cargo.toml`:
- [BLOCKING] Cargo.toml:70: Validate the competing branch before allowing destructive SPV rewind
  (existing thread: https://github.com/dashpay/platform/pull/5307#discussion_r4248258792)
  The pinned dash-spv revision introduces a destructive fork-recovery path that can be triggered by an unmatched peer header whose parent is an eligible stored block below the current tip. In the pinned upstream code, fork detection occurs before the normal header-validation path, and the resulting lifecycle event truncates the wallet, block headers, filters, and blocks before the competing branch has been checked for proof of work, expected difficulty, or superior chain work. A malicious connected peer can therefore force repeated shallow rewinds and resynchronization without mining a valid replacement chain. The TODO at packages/rs-platform-wallet/src/spv/runtime.rs:165-170 records the limitation but does not prevent the rewind. Pin a repaired upstream revision or apply an equivalent patch that validates branch eligibility before emitting the destructive fork event, and retain a regression proving that fabricated unmatched headers leave wallet and storage state unchanged.
Out-of-scope follow-up suggestions (3)

These are valid observations, but they are outside this PR's scope and should be handled in separate issues or author/maintainer-requested PRs rather than blocking this review.

  • Persist the rollback represented by ChainTruncated — WalletEvent::ChainTruncated still produces an empty CoreChangeSet, so persisted transaction rows, restored outpoints, confirmations, and synchronization heights can remain on the replaced branch after restart. This is a concrete wallet consistency gap introduced by the newly usable rewind path, but the PR explicitly excludes the cross-backend persistence migration and tracks it in #5380/#5384.
    • Follow-up: Land the ordered truncation changeset and SQLite, SwiftData, and Room persistence work before releasing a wallet build that enables this rewind behavior.
  • Join upstream auxiliary tasks before declaring SPV teardown complete — The pinned upstream client can leave detached failure or fork-recovery helpers holding client and storage references after stop reports completion, creating a transient directory-lock failure for immediate clear or restart. The helper ownership is private to dash-spv and the issue is tracked separately as #5381.
    • Follow-up: Adopt the upstream lifecycle fix and add an immediate clear-and-restart regression covering failure and fork races.
  • Stamp the new v4 coinbase blob format in the SQLite schema — Version-4 coinbase records add a serialized field while this branch retains SQLite schema V018, so an older binary can pass the schema gate and fail while decoding a newer transaction blob. The PR explicitly leaves the V019 migration to stacked #5150 and documents the interim compatibility window.
    • Follow-up: Land #5150's V019 migration and older-reader rejection coverage with any release that writes version-4 coinbase records.

@github-actions

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Waiting for bot review — coderabbitai not yet · thepastaclaw not yet. Wait for the missing reviews, or a writer can post /skip-bots to proceed without them; blocking findings still need addressing.
Full checklist in the description.

@lklimek
lklimek dismissed thepastaclaw’s stale review October 13, 2026 22:42

The blocking finding is the upstream dash-spv fork-recovery behaviour tracked as dashpay/rust-dashcore#1103. It is not introduced by a Platform-side change, is documented in this PR (TODO in spv/runtime.rs and the known limitations), and the PR owner accepted the risk until the upstream fix can be pinned. The readiness suggestion is recorded under known limitations.

This reverts commit 835f6af.

That commit worked around Test Suite failures after a quorum rotation:
on a "Quorum not found in cache" proof error it waited 65 seconds and
retried the proved read once. The failures were never caused by this
branch. v5.1-dev failed the same way before #5404, and the workaround
needed nothing from the rust-dashcore migration.

The causes are now fixed at the source and reach this branch through
the v5.1-dev merge (#5404):
- #5313: the SDK, including wasm-sdk, fetches a missing quorum key from
  the trusted quorum service and verifies the response again, instead of
  banning every node.
- #5354: dashmate reloads the local quorum list every second rather than
  every 60 seconds.
- quorum-list-server v0.8.0 lists every quorum Platform may still sign
  with (dashpay/quorum-list-server#16).

#5404's Test Suite passed without the retry. Keeping a fixed sleep keyed
on error text would only hide a regression of those fixes. The
standalone copy, #5420, was closed for the same reason.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@PastaPastaPasta

Copy link
Copy Markdown
Member

I pushed 9774c72d3c, which reverts 835f6af4bf (the test-suite retry for rotated quorums). It's a normal commit on top, not a force-push.

Why: the retry worked around Test Suite failures this branch didn't cause. v5.1-dev failed the same way before #5404, and the retry needed nothing from the rust-dashcore migration. The causes are now fixed at the source, and those fixes reach this branch through the v5.1-dev merge (#5404):

#5404's Test Suite passed without the retry. A fixed 65 s sleep keyed on error text would only hide a regression of those fixes. The standalone copy, #5420, was closed for the same reason.

The two test-suite files are back to their v5.1-dev versions, and the CHANGELOG entry is removed. Nothing else changes.


🤖 Posted autonomously by Claude on behalf of pasta.

@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Waiting for bot review — coderabbitai not yet · thepastaclaw ✓. Wait for the missing reviews, or a writer can post /skip-bots to proceed without them; blocking findings still need addressing.
Full checklist in the description.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review — Final validation — Phase 1 + Phase 2

The migration preserves the reviewed cryptographic, serialization, dependency, and storage compatibility behavior. Three actionable documentation and lifecycle issues remain: broadcast readiness can be reported after background SPV failure, the stop timeout does not cover lock acquisition or in-flight client operations, and Swift documentation describes teardown behavior that the native implementation no longer provides. Other reported concerns are explicitly deferred to stacked or upstream follow-ups.

🟡 3 suggestion(s)

2 finding(s) not shown inline (the lines are not part of this PR's diff)

🟡 Suggestion: Gate broadcast readiness on the retained client's running state
packages/rs-platform-wallet/src/spv/runtime.rs:271-272

The runtime intentionally retains the client after startup so it can be queried and stopped, but client ownership no longer proves that synchronization is running. After a background sync failure, is_running() becomes false while the client and the last peer snapshot can remain present; wait_until_ready() can therefore return true and the following broadcast is rejected by the stopped client. Include the existing running-state signal in this readiness predicate.

    async fn is_broadcast_ready(&self) -> bool {
        self.is_running() && !self.peer_tracker.snapshot().is_empty()
    }

source: muse-spark-1.3-contributor (phase1-reviewer: general, rust-quality); gpt-6.1-sol (phase2-reviewer: general, architecture-layering)

🟡 Suggestion: Update Swift SPV stop documentation for non-aborting teardown
packages/swift-sdk/Sources/SwiftDashSDK/PlatformWallet/PlatformWalletManagerSPV.swift:332-337

The current native implementation waits for the shutdown lock and in-flight client accesses before starting the bounded teardown wait. Once teardown starts, it waits up to 15 seconds for the owned task and returns shutdownIncomplete; it no longer has a separate 15-second client-stop budget or a two-second abort grace, and it never aborts upstream work. The Swift documentation therefore gives callers an incorrect approximately-32-second maximum and incorrectly promises abort escalation.

    /// After any in-flight client access releases its read lock, native teardown waits
    /// up to 15 s for the owned teardown task. It never aborts upstream work: if that
    /// task is still running, this method throws `shutdownIncomplete` and keeps the
    /// task tracked for a later stop. Waiting for the shutdown lock and in-flight
    /// client operations is not separately bounded, and an in-flight broadcast may
    /// add its acceptance timeout. Keep this call off the main actor.

source: gpt-6.1-sol (phase2-reviewer: general)

Review provenance

Source: reviewer 1: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 2: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 4: gpt-6.1-sol (agent: phase2-reviewer, role: ffi-engineer); reviewer 5: gpt-6.1-sol (agent: phase2-reviewer, role: platform-versioning); reviewer 6: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); reviewer 7: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 8: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); reviewer 9: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 10: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 11: gpt-6.1-sol (agent: phase2-reviewer, role: ffi-engineer); reviewer 12: gpt-6.1-sol (agent: phase2-reviewer, role: platform-versioning); reviewer 13: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 14: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)

  • Triage: critical by gpt-6.1-sol (effort low) — The large diff changes critical cryptographic and peer-facing serialization surfaces, including BLS and secp256k1 APIs, identity and quorum key handling, and legacy PlatformNodeId and coinbase payload encoding in packages/rs-dpp and related wallet/consensus code.
  • Phase 1 reviewers: muse-spark-1.3-contributor — general (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — rust-quality (completed, effort xhigh); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-3.8-flash-high (antigravity below 15% reserve: weekly 14% left, 5h 100% left), glm-5.3-flash (not used above high effort; tier asks max)
  • Single stage: Phase 1 and Phase 2 reviewed this head side by side, with no blocker gate between them (triage tier)
  • Fresh final gate: an independent Phase-2 review ran after iterative findings were reconciled
  • Fresh verifier: gpt-6.1-sol — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — ffi-engineer (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — platform-versioning (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — ffi-engineer (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — platform-versioning (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/rs-platform-wallet/src/spv/runtime.rs`:
- [SUGGESTION] packages/rs-platform-wallet/src/spv/runtime.rs:271-272: Gate broadcast readiness on the retained client's running state
  The runtime intentionally retains the client after startup so it can be queried and stopped, but client ownership no longer proves that synchronization is running. After a background sync failure, `is_running()` becomes false while the client and the last peer snapshot can remain present; `wait_until_ready()` can therefore return true and the following broadcast is rejected by the stopped client. Include the existing running-state signal in this readiness predicate.
- [SUGGESTION] packages/rs-platform-wallet/src/spv/runtime.rs:418-424: Bound SPV stop while waiting for in-flight lifecycle operations
  `SPV_STOP_TIMEOUT` only wraps the teardown task after `stop_locked` has acquired the shutdown mutex, taken the client write lock, and spawned the teardown task. A concurrent start can hold the shutdown mutex, while a client query or broadcast can hold the client read lock across an awaited operation; those waits occur before the timeout begins and can block `stop()` indefinitely. Since the FFI shutdown path waits for stop, a stalled query or lifecycle operation can prevent destruction from returning. Apply a cancellation-safe deadline to admission and lock acquisition, or otherwise allow shutdown to report a bounded failure without waiting indefinitely.

In `packages/swift-sdk/Sources/SwiftDashSDK/PlatformWallet/PlatformWalletManagerSPV.swift`:
- [SUGGESTION] packages/swift-sdk/Sources/SwiftDashSDK/PlatformWallet/PlatformWalletManagerSPV.swift:332-337: Update Swift SPV stop documentation for non-aborting teardown
  The current native implementation waits for the shutdown lock and in-flight client accesses before starting the bounded teardown wait. Once teardown starts, it waits up to 15 seconds for the owned task and returns `shutdownIncomplete`; it no longer has a separate 15-second client-stop budget or a two-second abort grace, and it never aborts upstream work. The Swift documentation therefore gives callers an incorrect approximately-32-second maximum and incorrectly promises abort escalation.
Out-of-scope follow-up suggestions (5)

These are valid observations, but they are outside this PR's scope and should be handled in separate issues or author/maintainer-requested PRs rather than blocking this review.

  • Persist the rollback represented by ChainTruncated — WalletEvent::ChainTruncated still produces an empty CoreChangeSet, so persisted transaction rows, restored outpoints, confirmation state, and sync heights can remain on the replaced branch after restart. This is a concrete wallet-integrity gap, but the PR explicitly excludes the persistence/accounting migration and documents the repair as #5380/#5384 stacked on this dependency update.
    • Follow-up: Land the ordered truncation changeset and atomic SQLite, SwiftData, and Room persistence changes before releasing a wallet that ships this SPV rollback path.
  • Validate competing SPV branches before destructive rewind — The pinned upstream dash-spv revision can begin fork recovery from a peer-supplied header before validating the competing branch's proof of work or chain work. The behavior is upstream-owned, explicitly marked with a TODO, and tracked as rust-dashcore#1103 rather than being repairable in this wrapper migration.
    • Follow-up: Pin a dash-spv revision containing the upstream fork-recovery validation fix and add an invalid-header regression.
  • Join detached upstream SPV helper tasks before reopening storage — Upstream fork and failed-sync helper tasks can retain client and storage references after the locally awaited stop completes, allowing an immediate clear or restart to encounter a directory lock. Those helpers are private to dash-spv, and the limitation is already tracked separately as #5381.
    • Follow-up: Add an upstream ownership barrier that joins all helper tasks before dash-spv reports stop completion.
  • Reject quorum types before narrowing them to u8 — get_quorum_public_key_with_status still casts the public u32 quorum type directly to u8, so values above 255 alias another LLMQ type. This predates the migration and is explicitly documented as a separate follow-up rather than being introduced by the dependency update.
    • Follow-up: Validate the conversion with u8::try_from at the wallet-runtime API boundary.
  • Gate the new transaction-record blob format with a SQLite schema version — Version-4 coinbase records add a serialized field while this branch remains at schema V018. The PR explicitly defers the V019 schema gate to stacked #5150, which supplies the migration and older-reader rejection.
    • Follow-up: Land #5150's V019 migration together with this pin before enabling the forward-format wallet release.

Comment on lines +418 to +424
/// Stop SPV sync, waiting up to 15 seconds for startup and teardown.
///
/// **Every phase after taking the client is bounded** — `stop` runs on the
/// path [`PlatformWalletManager::shutdown`](crate::manager::PlatformWalletManager::shutdown)
/// and therefore the FFI's `destroy` must return through, so a wedged
/// host callback has to surface as an error rather than hang teardown:
/// the client stop is capped at [`SPV_CLIENT_STOP_BUDGET`], the run-loop
/// join at [`SPV_STOP_TIMEOUT`] with an [`SPV_ABORT_GRACE`] post-abort
/// confirmation. A run loop that outlives all of that is **re-parked**,
/// not detached, so a teardown retry re-joins it — and the error return
/// keeps it out of a clean shutdown verdict.
/// A timeout or cancelled caller leaves teardown tracked for the next stop.
/// Restart stays blocked until a stop confirms completion. Client queries
/// already in flight finish before teardown starts; this wait is unbounded.
///
/// Taking the client is not bounded: it waits for the client's read
/// guards, and [`broadcast_transaction_and_wait`](Self::broadcast_transaction_and_wait)
/// holds one for its whole acceptance wait (the caller's timeout). That
/// keeps a broadcast on a live client and the data directory free once
/// `stop` returns, at the cost of a stop waiting for the broadcast.
/// # Errors

@thepastaclaw thepastaclaw Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⏭️ Deferred (maintainer decision; not blocking) at 9774c72d; see the replies below.

🟡 Suggestion: Bound SPV stop while waiting for in-flight lifecycle operations

SPV_STOP_TIMEOUT only wraps the teardown task after stop_locked has acquired the shutdown mutex, taken the client write lock, and spawned the teardown task. A concurrent start can hold the shutdown mutex, while a client query or broadcast can hold the client read lock across an awaited operation; those waits occur before the timeout begins and can block stop() indefinitely. Since the FFI shutdown path waits for stop, a stalled query or lifecycle operation can prevent destruction from returning. Apply a cancellation-safe deadline to admission and lock acquisition, or otherwise allow shutdown to report a bounded failure without waiting indefinitely.

source: gpt-6.1-sol (phase2-reviewer: general, architecture-layering, ffi-engineer, platform-versioning, rust-quality, security-auditor)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not changed here. This is the documented contract of the stop path, not an oversight: SpvRuntime::stop says "Client queries already in flight finish before teardown starts; this wait is unbounded", and the 15 s budget is stated for startup and teardown only. That lifecycle came in with #5311 and #5322, which were reviewed on their own and merged into this branch; this PR is the dependency migration and does not revisit it.

Bounding admission and lock acquisition is a real follow-up, but it is a design change (what a timed-out stop leaves behind while a query still holds the client) rather than a fix to fold into the pin update. The remaining teardown window is tracked in #5381.

🤖 Co-authored by Claudius the Magnificent AI Agent

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I verified that stop explicitly documents the unbounded in-flight-query wait and that its locking/timeout sequence is inherited from #5311/#5322, so I accept deferring admission deadlines to a lifecycle follow-up rather than requiring that design change in this dependency migration.

Noted as intentionally deferred; I will not press it further here.

@PastaPastaPasta PastaPastaPasta left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I spent a good bit of time on this and reviewing and improving it; I think this is ready to merge.

@github-actions

Copy link
Copy Markdown
Contributor

@coderabbitai review

No review for 9774c72d yet, so PR Hygiene is asking once. If nothing arrives, the requirement is dropped for this commit and the pull request is labelled bot-review-skipped.

@lklimek
lklimek merged commit 3b5e4a7 into v5.1-dev Oct 14, 2026
51 of 52 checks passed
@lklimek
lklimek deleted the chore/rust-dashcore-1112-v5.1 branch October 14, 2026 07:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

waiting-bots Waiting for the review bots to report on this head

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants